/*
 * This file is part of the eccOS Library.
 * Copyright (C) 2007.
 *
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 */
package org.openeccos.table;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Vector;

import nextapp.echo2.app.Component;
import nextapp.echo2.app.Label;
import nextapp.echo2.app.Table;
import nextapp.echo2.app.event.ActionListener;
import nextapp.echo2.app.event.ChangeEvent;
import nextapp.echo2.app.event.ChangeListener;
import nextapp.echo2.app.table.TableCellRenderer;

import org.openeccos.dao.HeaderValue;
import org.openeccos.dao.IPageableDataSource;
import org.openeccos.dao.PagableResult;
import org.openeccos.gui.EccLE;
import org.openeccos.gui.EccLE.DISPLAY_MODE;
import org.openeccos.internal.EccleEditorPane;
import org.openeccos.util.IPageableFactory;

import com.sas.framework.expojo.ModelExposer;

public class EccleTableModel extends BaseTableModel implements TableCellRenderer {

	public final static int ROW_EXPANDED_OR_COLLAPSED_EVENT = 5;

	protected int rowsPerPage;
	protected IPageableFactory pageableFactory;
	protected int currentTopRow = 0;
	protected LinkedHashMap<Serializable, EccleEditorPane> expansionGroup = new LinkedHashMap<Serializable, EccleEditorPane>();
	protected List<HeaderValue> modelItems = new Vector<HeaderValue>();
	protected int totalRows;
	protected EccLE.DISPLAY_MODE displayMode;
	protected ActionListener dataChangeListener;

	public EccleTableModel(IPageableFactory pageableFactory, EccLE.DISPLAY_MODE displayMode, ActionListener dataChangeListener) {
		this.pageableFactory = pageableFactory;
		this.displayMode = displayMode;
		this.dataChangeListener = dataChangeListener;
		rowsPerPage = pageableFactory.getPageSizeCollapsed();
	}

	public void add(HeaderValue headerValue) {
		modelItems.add(headerValue);
		fireTableRowsInserted(modelItems.size(), modelItems.size());
	}
	
	public void clear() {
		modelItems.clear();
	}

	private void expandableStateChanged(EccleEditorPane section) {
		if (section.isExpanded()) {
			rowsPerPage = pageableFactory.getPageSizeExpanded();
			int rowsToHideAtEnd = pageableFactory.getPageSizeCollapsed() - rowsPerPage;
			int rowsToHideAtTop = 0;
			if (section.getIndex() + rowsToHideAtEnd >= pageableFactory.getPageSizeCollapsed()) {
				rowsToHideAtTop = rowsToHideAtEnd - pageableFactory.getPageSizeCollapsed() + section.getIndex() + 1;
				rowsToHideAtEnd -= rowsToHideAtTop;
				rowsToHideAtTop++;  //add one to hide at the top, because the current ection never gets hidden
			}			
			int count = 0;
			for (EccleEditorPane editor : expansionGroup.values()) {
				if (section == editor) continue;
				if (editor.isExpanded()) {
					editor.setExpanded(false);
				}
				if (++count < rowsToHideAtTop || count >= pageableFactory.getPageSizeCollapsed() - rowsToHideAtEnd) {
					editor.setVisible(false);
				}
			}
		} else {
			// Collapsing..
			for (EccleEditorPane editor : expansionGroup.values()) {
				editor.setVisible(true);
			}
		}
	}

	public int getAllRowsCount() {
		return totalRows;
	}

	public int getColumnCount() {
		return 1;
	}

	public int getCurrentTopRow() {
		return currentTopRow;
	}

	public int getRowCount() {
		return modelItems.size();
	}

	public Component getTableCellRendererComponent(Table table, Object value, int col, final int row) {
		if (!(value instanceof HeaderValue)) {
			return new Label(value + " is not instance of HeaderValue");
		}
		HeaderValue headerValue = (HeaderValue)value;
		Component component = null;
		if (DISPLAY_MODE.EXPANDABLE.equals(displayMode)) {
			EccleEditorPane section = expansionGroup.get(headerValue.identifier);
			if (section == null) {
				section = pageableFactory.getEditor(headerValue);
				final EccleEditorPane finalSection = section;
				if (((HeaderValue)value).isEditing) {
					section.btnEditClicked();
				}
				section.getExpansionModel().addChangeListener(new ChangeListener() {
					public void stateChanged(ChangeEvent changeEvent) {
						expandableStateChanged(finalSection);
					}
				});
				section.setDataChangedListener(dataChangeListener);
			}
			section.setVisible(headerValue.isVisible);
			expansionGroup.put(((HeaderValue)value).identifier, section);
			section.setIndex(row);
			component = section;
		} else {
			component = pageableFactory.getNavigatorCell((HeaderValue)value);
		}
		return component;
	}

	public Object getValueAt(int col, int row) {
		if (modelItems.size() <= row) {
			return null; // should never happen...
		}
		HeaderValue headerValue = modelItems.get(row);
		headerValue.row = row;
		return headerValue;
	}

	public void loadData() {
		modelItems.clear();
		expansionGroup.clear();
		rowsPerPage = pageableFactory.getPageSizeCollapsed();
		
		IPageableDataSource repo = (IPageableDataSource)ModelExposer.get().getRepository(pageableFactory.getDataSourceName());
		PagableResult result = repo.getResult(currentTopRow, rowsPerPage, pageableFactory.getFilterParameters());

		int count = 0;
		for (HeaderValue headerValues : result.getHeaders()) {
			if (headerValues.isEditing) {
				rowsPerPage = pageableFactory.getPageSizeExpanded();
			}
			//hide the sections at the end when when one section is opened
			headerValues.isVisible = count++ < rowsPerPage;
			modelItems.add(headerValues);
		}
		totalRows = result.getRowCount();

		// If we're paginating outside the total number of records, then go to the last page.
		if (currentTopRow > totalRows) {
			currentTopRow = (int) (totalRows - rowsPerPage);
		}

		fireTableDataChanged();
	}

	public void setCurrentTopRow(int topRow) {
		currentTopRow = topRow;
		rowsPerPage = pageableFactory.getPageSizeCollapsed();
		loadData();
	}

	public void setRowsPerPage(int rowsPerPage) {
		this.rowsPerPage = rowsPerPage;
	}

	public int toUnpagedModelRowIndex(int row) {
		return currentTopRow + row;
	}

	@Override
	public void setValueAt(Object value, int column, int row) {
		modelItems.set(row, (HeaderValue)value);
	}

	public int getRowsCollapsed() {
		return pageableFactory.getPageSizeCollapsed();
	}
}